<?php

namespace App\Services;

use App\Models\HallPartnerConfig;
use App\Models\Money;
use App\Models\MoneyLog;
use App\Models\MoneyPartner;
use App\Models\MoneyTransfer;
use App\models\Partner;
use App\Models\PartnerUser;
use common\utils\SysCode;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpFoundation\Request;
use App\Services\BaseService;
use App\Models\MoneyPartnerLog;

class FundsService
{
    //最大额度转化金额
    const MAX_EXCHANGE_AMOUNT = 9999999;

    //主转子
    const EXCHANGE_TYPE_ONE = 1;

    //子转主
    const EXCHANGE_TYPE_TWO = 2;

    //主转子
    const EXCHANGE_PRIMARY_TO_CHILDWALLET = 301;
    //子转主
    const EXCHANGE_CHILDWALLET_TO_PRIMARY = 302;

    //收入
    const MONEY_LOG_CATEGORY_INCOME = 1;
    //支出
    const MONEY_LOG_CATEGORY_EXPEND = 2;
    //额度转换
    const MONEY_LOG_CATEGORY_TRANSFORM = 3;
    //子钱包同步
    const MONEY_PARTNER_LOG_CATEGORY_SYNC = 3;

    //额度转化
    public static function exchange($user, $params)
    {

        $cid = $params['cid'];//partner_id
        $type = $params['type'];//1-主转子2-子转主
        $amount = $params['amount'];//金额 实际金额
        $checkoutAmount = floatval($amount);
        $userId = $user['id'];
        $username = $user['username'];
        $amount = amount_format($amount, true);//第三方api全部是厘单位转化金额

        //判断是厅否存在对应的子钱包
        $partner = HallPartnerConfig::where(['partner_id' => $cid, 'status' => 1])->first();

        if (empty($partner)) {
            return error_exit(SysCode::BANK_EXCHANGE_WALLET_NOT_EXIT);
        }

        //检测系统是否开放额度转化功能
        $setting = HomeService::globalData();
        if (isset($setting['base']['transfer']) && $setting['base']['transfer'] == false) {
            return error_exit(SysCode::BANK_EXCHANGE_NOT_OPEN);
        }

        //检测金额的系统范围
        if (!is_numeric($amount) || empty($checkoutAmount) || $checkoutAmount > self::MAX_EXCHANGE_AMOUNT || $checkoutAmount <= 0) {
            return error_exit(SysCode::BANK_EXCHANGE_AMOUNT_ERROR, ['amount' => $amount]);
        }

        //判断主钱包
        $wallet = Money::where(['user_id' => $userId])->first();

        if (empty($wallet)) {
            return error_exit(SysCode::BANK_EXCHANGE_MISS_WALLET, ['type' => $type == 1 ? '主转子' : '子转主', 'name' => '主钱包', 'data' => $cid]);
        }

        //检测并创建子钱包和第三方账号(第三方登陆或者额度转化时候检测账号和子钱包)
        GameService::createUser($user, $partner['partner_name']);

        //判断子钱包是否存在
        $childWallet = MoneyPartner::where('partner_id', '=', $cid)->where('user_id', '=', $userId)->first();
        if (empty($childWallet)) {
            return error_exit(SysCode::BANK_EXCHANGE_WALLET_NOT_EXIT);
        }

        //同步第三方余额


        static::sysncChildWalletBalance($childWallet['partner_name'], $user);

        $amountData = intval($amount);
        $primaryBalance = $wallet['balance'];
        $childBalance = $childWallet['balance'];

        //判断钱包余额是否足够
        if ($amountData && $type == self::EXCHANGE_TYPE_ONE && $amountData > $primaryBalance) {
            return error_exit(SysCode::BANK_EXCHANGE_BALANCE_UNOUGH, ['type' => '主钱包', 'balance' => $primaryBalance, 'amount' => $amount]);
        }

        if ($amount && $type == self::EXCHANGE_TYPE_TWO && $amountData > $childBalance) {
            return error_exit(SysCode::BANK_EXCHANGE_BALANCE_UNOUGH, ['type' => '子钱包', 'balance' => $childBalance, 'amount' => $amount]);
        }

        $orderNumber = static::geneRateDealNumber();
        if ($type == self::EXCHANGE_TYPE_ONE) {
            $remark = "主钱包向" . $childWallet['name'] . "转账，转账单号：" . $orderNumber;
        } else {
            $remark = $childWallet['name'] . "向主钱包转账，转账单号：" . $orderNumber;
        }
        //写入转账记录
        $transformLog = [
            'user_id' => $user['id'],
            'username' => $user['username'],
            'money_id' => $wallet['id'],
            'money_partner_id' => $cid,
            'money_partner_name' => $childWallet['partner_name'],
            'type' => $type,
            'amount' => $amountData,
            'order_number' => $orderNumber,
            'operater_admin_id' => 0,
            'status' => 0,
            'remark' => $remark,
        ];

        $insertModel = new MoneyTransfer($transformLog);

        if (!$insertModel->save()) {
            Log::error('User Exchange Error:', (array)$insertModel);
        }
        $userthird = PartnerUser::where(['user_id' => $userId, 'partner_id' => $cid])->first();

        try {
            $gameType = strtolower($childWallet['partner_name']);

            //同步后在进行一次余额判断 防止第三方和我们钱包不一致
            if ($type == self::EXCHANGE_TYPE_TWO) {
                $checkRealBalance = MoneyPartner::where('partner_id', '=', $cid)->where('user_id', '=', $userId)->first();
                if ($checkRealBalance['balance'] < $amount) {
                    return error_exit(SysCode::BANK_EXCHANGE_THIRD_MONEY_ERROR);
                }
            }

            $ret = GameService::transfer($user, $gameType, $orderNumber, $type == 1 ? $amount : -$amount);

            if (!isset($ret['code'])) {
                return error_exit(SysCode::BANK_EXCHANGE_GETBALANCE_MONEY_ERROR);
            } else {
                if (($ret['code'] != SysCode::OK)) {
                    $msg = $type == self::EXCHANGE_TYPE_ONE ? "订单号为：{$orderNumber}{$gameType}主子钱包转换,子钱包转入失败！" : "订单号为：{$orderNumber}{$gameType}主子钱包转换,子钱包转出失败！";
                    Log::error('User Exchange Error:' . $msg);
                    return error_exit(SysCode::BANK_EXCHANGE_GETBALANCE_MONEY_ERROR);
                } else {
                    self::updateTranferLogstatus($userId, $orderNumber, 3, '第三方转账成功');
                }
            }


        } catch (\Throwable $e) {
            Log::error("User Exchange Error:", [$e->getMessage() . ", 转账订单号{$orderNumber}参数为:" . json_encode($params, JSON_UNESCAPED_UNICODE)]);
            //更新转账订单为失败(接口返回)
            self::updateTranferLogstatus($userId, $orderNumber, 2, $e->getCode() . ", msg:" . $e->getMessage());
            return error_exit(SysCode::BANK_EXCHANGE_GETBALANCE_MONEY_ERROR);
        }

        //修改订单的状态
        $res = self::updateTranferLogstatus($userId, $orderNumber, 3, "第三方转账成功");

        if (empty($res)) {
            return error_exit(SysCode::BANK_EXCHANGE_GETBALANCE_MONEY_ERROR);
        }


        //开启事务
        DB::beginTransaction();
        try {
            $diffPrimary = $type == self::EXCHANGE_TYPE_ONE ? $primaryBalance - $amount : $primaryBalance + $amount;
            //修改主钱包余额
            $updataPrimaryMoney = Money::where(['user_id' => $userId])->first();
            $update = [
                'balance' => $diffPrimary
            ];
            if (!$updataPrimaryMoney->update($update)) {
                Log::error('用户id为' . $userId . '转账订单号为' . $orderNumber . "修改主钱包余额失败！参数为:" . json_encode(['userId' => $userId, 'no' => $orderNumber, 'amount' => $amount * 100], JSON_UNESCAPED_UNICODE));
                self::updateTranferLogstatus($userId, $orderNumber, 4, "更新主钱包失败");
                DB::rollBack();
                return error_exit(SysCode::BANK_EXCHANGE_GETBALANCE_MONEY_ERROR);
            }


            //修改子钱包余额
            $updateChildWallet = MoneyPartner::where('partner_id', '=', $cid)->where('user_id', '=', $userId)->first();

            $diffChildWallet = $type == 1 ? $updateChildWallet['balance'] + $amount : $updateChildWallet['balance'] - $amount;
            $updatePartner = [
                'balance' => $diffChildWallet
            ];

            $checkUpdate = MoneyPartner::where('partner_id', '=', $cid)->where('user_id', '=', $userId)->update($updatePartner);
            if ($checkUpdate['code'] != SysCode::OK) {
                Log::error('用户id为' . $userId . '转账订单号为' . $orderNumber . "修改子钱包余额失败！参数为:" . json_encode(['userId' => $userId, 'no' => $orderNumber, 'amount' => $amount], JSON_UNESCAPED_UNICODE));
                self::updateTranferLogstatus($userId, $orderNumber, 4, "更新子钱包失败");
                DB::rollBack();
                return error_exit(SysCode::BANK_EXCHANGE_GETBALANCE_MONEY_ERROR);
            }

            //增加资金流水（funds_deal_log）
            $deal_type = $type == self::EXCHANGE_TYPE_ONE ? self::EXCHANGE_PRIMARY_TO_CHILDWALLET : self::EXCHANGE_CHILDWALLET_TO_PRIMARY;
            $insertArray = [
                'user_id' => $userId,
                'username' => $username,
                'order_number' => $orderNumber,//流水号
                'category' => self::MONEY_LOG_CATEGORY_TRANSFORM,
                'type' => $deal_type,//流水类型
                'amount' => $amount,
                'balance_before' => $primaryBalance,//实际变动金额(正值)
                'balance' => $diffPrimary,//变动后的余额
                'memo' => $remark//备注
            ];
            $insertMoneyLog = new MoneyLog($insertArray);
            if (!$insertMoneyLog->save()) {
                Log::error('用户id为' . $userId . '转账订单号为' . $orderNumber . "添加流水记录失败！,日志参数为:" . json_encode($insertArray, JSON_UNESCAPED_UNICODE));
                self::updateTranferLogstatus($userId, $orderNumber, 4, "添加流水日志失败");
                DB::rollBack();
                return error_exit(SysCode::BANK_EXCHANGE_GETBALANCE_MONEY_ERROR);
            }
            DB::commit();
        } catch (\Throwable $e) {
            self::updateTranferLogstatus($userId, $orderNumber, 4, $e->getMessage());
            DB::rollBack();
            return error_exit(SysCode::BANK_EXCHANGE_GETBALANCE_MONEY_ERROR);
        }
        //修改订单的状态
        $res = self::updateTranferLogstatus($userId, $orderNumber, 1, "");
        if (empty($res)) {
            return error_exit(SysCode::BANK_EXCHANGE_GETBALANCE_MONEY_ERROR);
        }
        return SysCode::OK;
    }


    /**
     * 修改转账订单状态
     * @param $userId
     * @param $tradeNo
     * @param $status
     * @return int
     * @throws yii\db\Exception
     */
    public static function updateTranferLogstatus($userId, $tradeNo, $status, $remark)
    {
        $updateMoneyTransform = MoneyTransfer::where(['user_id' => $userId, 'order_number' => $tradeNo]);
        $update = [
            'status' => $status,
            'remark' => $remark,
        ];
        $rel = $updateMoneyTransform->update($update);
        if (empty($rel)) {
            Log::error("转账订单号为{$tradeNo}修改成功状态失败！,参数为：" . json_encode(['userId' => $userId, 'no' => $tradeNo, 'status' => 1], JSON_UNESCAPED_UNICODE));
        }
        return $rel;
    }


    /**
     * 同步第三方余额
     * @param $gameType string 游戏类型
     * @param $lastBalance int 上次余额
     * @param $childWalletId  int 钱包id
     * @param $userId int  用户id
     */
    public static function sysncChildWalletBalance($gameType, $user)
    {
        $userID = $user['id'];
        $gameType = strtoupper($gameType);
        $ret = GameService::getBalance($user, $gameType);
        if (isset($ret['code']) && $ret['code'] != 0) {
            return error_exit(SysCode::BANK_EXCHANGE_GETBALANCE_ERROR);
        }
        $balance = $ret['data'];

        $mp = MoneyPartner::where(['user_id' => $user['id'], 'partner_name' => $gameType])->first();
        if (empty($mp)) {
            Log::error('未找到对应的子钱包:', ['user_id' => $user['id'], 'partner_name' => $gameType]);
            return error_exit(SysCode::BANK_EXCHANGE_WALLET_NOT_EXIT);
        }
        // 余额相同，不需要继续处理
        if ($mp['balance'] == $balance) {
            return;
        }

        DB::beginTransaction();
        try {
            // 强制以第三方API获取到的余额为准
            $mp->update(['balance' => $balance]);
            MoneyPartnerLog::insert([
                'money_partner_id' => $mp['id'],
                'user_id' => $user['id'],
                'category' => self::MONEY_PARTNER_LOG_CATEGORY_SYNC,
                'balance' => $balance,
                'balance_before' => DB::raw('balance_before=balance'),
                'amount' => DB::raw("amount=($balance-balance)"),
            ]);
            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('updateMoneyParter Error:' . $e->getMessage());
            return error_exit(SysCode::BANK_EXCHANGE_WALLET_NOT_EXIT);
        }
    }

    /**资金流水号
     * @param int $rand
     * @param int $length
     * @return string
     */
    public static function geneRateDealNumber($rand = 99999, $length = 5)
    {
        return date('Ymdhis') . str_pad(mt_rand(1, $rand), $length, '0', STR_PAD_LEFT);
    }

}

